#pragma once
#include <queue>
#include <iostream>
#include <pthread.h>

using namespace std;

// 先描述，再组织。                                                //（两个阻塞队列时不一样的）
// 一个阻塞队列，需要实现  push任务  pop任务  互斥访问队列(mutex)  阻塞生产者(consumer_cond)   阻塞消费者(producer)
//该阻塞队列阻塞的是整个队列本身，是已经适配多生产消费者的情况，处理3，2，1原则，处理三种关系，两个角色，一个场所

template <class T, size_t N = 5>
class BlockQueue
{
public:
    BlockQueue()
        : _capacity(N)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_consumer_cond, nullptr);
        pthread_cond_init(&_producer_cond, nullptr);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_consumer_cond);
        pthread_cond_destroy(&_producer_cond);
    }

    // 生产者添加任务
    void push(const T &task) // 注意不是if else结构哦，是该线程一直循环问，等不阻塞了就接着下面放任务或者拿任务了
    {
        pthread_mutex_lock(&_mutex);
        while (_bq.size() == _capacity)
        {
            // 阻塞生产者
            pthread_cond_wait(&_producer_cond,&_mutex);
        }
        // 放任务，唤醒消费者
        _bq.push(task);
        pthread_cond_signal(&_consumer_cond); // signal唤醒线程，即使该阻塞队列没有线程，也不会报错
        // cout << "任务已放入，即刻唤醒消费者" << endl;
        pthread_mutex_unlock(&_mutex);
    }

    // 消费者拿出任务
    T pop() // 不是if else结构是if结构，后面还得执行的，同时为保证安全，用while代替if
    {
        pthread_mutex_lock(&_mutex);
        while (_bq.size() <= 0)
        {
            // 当前没有任务，阻塞消费者
            pthread_cond_wait(&_consumer_cond,&_mutex);//进入阻塞后，会自己释放该锁，回来又会自己重新申请
        }
        // 消费者拿出任务
        T temp = _bq.front();
        _bq.pop();

        pthread_mutex_unlock(&_mutex);
        return temp;
    }

private:
    queue<T> _bq;
    size_t _capacity;
    pthread_mutex_t _mutex;
    pthread_cond_t _consumer_cond;
    pthread_cond_t _producer_cond;
    // size_t size;         //size不是很需要，因为vector就能直接表示
};